package com.dragon.english_practice_back.service.impl;


import com.dragon.english_practice_back.entity.LoginUserDetails;
import com.dragon.english_practice_back.entity.User;
import com.dragon.english_practice_back.enums.RedisPrefixEnum;
import com.dragon.english_practice_back.mapper.RoleMapper;
import com.dragon.english_practice_back.mapper.UserMapper;
import com.dragon.english_practice_back.service.AuthenticationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class AuthenticationUtilImpl implements AuthenticationUtil {

    RedisTemplate<String, LoginUserDetails> redisTemplateLoginUserDetails;
    UserMapper userMapper;
    RoleMapper roleMapper;
    @Value("${redis.key.security.time:120}")
    int time;

    AuthenticationUtilImpl(RedisTemplate<String, LoginUserDetails> redisTemplateLoginUserDetails,
                           UserMapper userMapper,
                           RoleMapper roleMapper
    ){
        this.redisTemplateLoginUserDetails = redisTemplateLoginUserDetails;
        this.userMapper = userMapper;
        this.roleMapper = roleMapper;
    }

    //获取身份id
    public int id(){
        return ((LoginUserDetails)this.getAuthentication().getPrincipal()).getUser().getId();
    }

    //获取当前使用的对象
    public User getUser(){
        return ((LoginUserDetails)this.getAuthentication().getPrincipal()).getUser();

    }

    public LoginUserDetails getLoginUserDetails(){
        return ((LoginUserDetails)(this.getAuthentication().getPrincipal()));
    }

    public Authentication getAuthentication(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication == null) throw new UsernameNotFoundException("not authentication");
        return authentication;
    }

    @Override
    public boolean cacheUserData(LoginUserDetails loginUserDetails) {
        //将用户信息存放至redis里
        redisTemplateLoginUserDetails.opsForValue().set(RedisPrefixEnum.LOGIN_USER_DETAIL.getPrefix()+ loginUserDetails.getUser().getId() , loginUserDetails, time, TimeUnit.MINUTES);

        return true;
    }

    @Override
    public boolean unCacheUserData() {

        return redisTemplateLoginUserDetails.delete(RedisPrefixEnum.LOGIN_USER_DETAIL.getPrefix() + id());
    }

    @Override
    public boolean loadUserData(Integer id) {
        //取出用户信息
        LoginUserDetails loginUserDetails = redisTemplateLoginUserDetails.opsForValue().get(RedisPrefixEnum.LOGIN_USER_DETAIL.getPrefix() + id);
        //如果redis内的用户信息已过期, 则根据id查询数据库
        if(loginUserDetails == null){
            log.info("redis内的用户信息已过期, 根据id查询数据库, id:{}", id);

            //查询数据库
            User user = userMapper.selectById(id);
            //如果用户不存在, 则抛出异常
            if(user == null){
                throw new UsernameNotFoundException("用户不存在");
            }
            //清除密码
            user.setPassword(null);

            loginUserDetails = new LoginUserDetails();
            loginUserDetails.setUser(user);
            //插入角色信息
            loginUserDetails.setRole(this.roleMapper.roleStringsByUserId(user.getId()));
        }

        //加载用户信息
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(loginUserDetails, null, loginUserDetails.getAuthorities()));
        //更新存活时间
        redisTemplateLoginUserDetails.opsForValue().set(RedisPrefixEnum.LOGIN_USER_DETAIL.getPrefix()+ id, loginUserDetails, time, TimeUnit.MINUTES);

        return true;
    }
}
